Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: usb: udc_stm32: Handle HAL callbacks on separate thread #80820

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

topisani
Copy link
Contributor

@topisani topisani commented Nov 4, 2024

All HAL callback handling is offloaded to a separate thread.
Tested on STM32H747 (arduino portenta) with the high speed controller.

This is an attempt at fixing #61464.
Marked as a draft, as i would at least like to do some minor stylistic cleanup before submitting, but if anyone could help test, I would appreciate it. As far as i can tell, no additional locking should be required, and no timing invariants are broken, but I could use some extra eyes on that as well.

@erwango
Copy link
Member

erwango commented Nov 4, 2024

A huge thanks. We'll for sure help testing on extended sample of series.

@GeorgeCGV
Copy link
Collaborator

Is it safe to enqueue a stack-allocated msg pointer?

Perhaps the event approach is safer. Given that the combined number of IN and OUT endpoints is typically less than 32 (15 IN + 15 OUT), a k_event could be used to notify about IN, OUT, or SETUP.

Additionally, this approach helps prevent potential message queue overflows that might result in missed event/message handling and supports ordered processing.

@topisani
Copy link
Contributor Author

Is it safe to enqueue a stack-allocated msg pointer?

Yes, it is copied into the ringbuffer.

Perhaps the event approach is safer. Given that the combined number of IN and OUT endpoints is typically less than 32 (15 IN + 15 OUT), a k_event could be used to notify about IN, OUT, or SETUP.

Isnt it technically up to 16+16 endpoints including the control endpoint?

Additionally, this approach helps prevent potential message queue overflows that might result in missed event/message handling and supports ordered processing.

While I agree about the message queue overflows, I don't see how k_event would support ordered processing? Thats mostly why I chose the msgq based approach, to make sure the events were processed in the order they arrived

@GeorgeCGV
Copy link
Collaborator

Not trying to block, just discussing out of curiosity.

Yes, it is copied into the ringbuffer.

Apologies, I just noticed the message is passed not a pointer; all good.

Isnt it technically up to 16+16 endpoints including the control endpoint?

That fits.

I don't see how k_event would support ordered processing?

Not in a fifo way, but In the processing. I.e. process setup before in (process x before y).

@topisani
Copy link
Contributor Author

topisani commented Nov 19, 2024

Not in a fifo way, but In the processing. I.e. process setup before in (process x before y).

Ah, I dont think this is what we would want though. All of these callbacks are triggered from the same IRQ, i.e. have the same priority, and should be processed in the order they arrived. It should maybe be considered how the ordering interdependency with the udc_submit_event processing could impact things, but i think that requires a deeper knowledge of the STM32 USB stack than I have. I would personally be ok to leave this up to testing too.

@erwango I believe it is ready for testing. Is there anything else I can do to help the testing effort along?

@erwango
Copy link
Member

erwango commented Nov 19, 2024

@topisani It's failing on disco_l475_iot1. Before:

*** Booting Zephyr OS build v4.0.0-405-g2efc8598e39f ***
[00:00:00.010,000] <inf> cdc_acm_echo: USB device support enabled
[00:00:00.010,000] <inf> cdc_acm_echo: Wait for DTR
[00:00:25.413,000] <inf> cdc_acm_echo: USBD message: Device suspended
[00:00:25.568,000] <inf> cdc_acm_echo: USBD message: Bus reset
[00:00:25.683,000] <inf> cdc_acm_echo: USBD message: Bus reset
[00:00:25.760,000] <inf> cdc_acm_echo: USBD message: New device configuration
[00:00:25.761,000] <err> udc_stm32: ep 0x00 queue is empty
[00:00:25.761,000] <inf> cdc_acm_echo: USBD message: CDC ACM line coding
[00:00:25.761,000] <inf> cdc_acm_echo: Baudrate 9600

After:

*** Booting Zephyr OS build v4.0.0-409-g84574a1c389f ***
[00:00:00.010,000] <err> usbd_init: Failed to assign endpoint addresses
[00:00:00.010,000] <err> usbd_init: Failed to init HS configuration 1
[00:00:00.010,000] <err> usbd_sample_config: Failed to initialize device support
[00:00:00.010,000] <err> cdc_acm_echo: Failed to initialize USB device
[00:00:00.010,000] <err> cdc_acm_echo: Failed to enable USB

@topisani
Copy link
Contributor Author

@erwango Alright, i believe this is my attempt at detecting USB HS support that fails. I have an l432 board i can test on, if that is the case, it should fail there as well

@topisani
Copy link
Contributor Author

topisani commented Nov 20, 2024

I could reproduce the issue on a nucleo_l432kc, and it appears to have been the HS detection. It now follows the maximum-speed dts prop, like in most of the other udc drivers, which fixed the issue for me.

@topisani
Copy link
Contributor Author

topisani commented Dec 2, 2024

Alright, i did some testing using samples/subsys/usb/cdc_acm -DCONF_FILE=usbd_next_prj.conf on the various boards i could find.
test: open the cdc_acm terminal, and type characteres. The device should echo each character back.

nucleo_u575zi_q: PASS
stm32f469i_disco: PASS
stm32f769i_disco: FAIL

I have also done a test on an arduino_portenta_h7 based custom board, that also failed. The common factor is that both boards use HS USB.

Checking with main, these boards also fail in the same way, so I believe it is unrelated to these changes, that the current ucd_stm32 does not correctly implement usbotg_hs support. I will look at a fix separately from this PR.

@topisani topisani force-pushed the udc-stm32 branch 2 times, most recently from 2275c22 to 2a056c0 Compare December 2, 2024 16:09
@topisani topisani marked this pull request as ready for review December 2, 2024 16:37
@fpistm
Copy link
Contributor

fpistm commented Dec 3, 2024

Hi @topisani

I've started to test on my side, here my results using samples/subsys/usb/cdc_acm with -DCONF_FILE=usbd_next_prj.conf and sent a lorem ipsum buffer of 1024 bytes several times.

series boards controller cdc_acm sample plug/unplug
F2 nucleo_f207zg st,stm32-otgfs
F4 nucleo_f429zi st,stm32-otgfs
F7 stm32f746g_disco st,stm32-otgfs
H7 nucleo_h723zg st,stm32-otghs
L4 b_l4s5i_iot01a st,stm32-otgfs
U5 nucleo_u575zi_q st,stm32-otgfs

I will continue tests and report here.

@@ -912,6 +913,7 @@ static const struct udc_stm32_config udc0_cfg = {
.pma_offset = USB_BTABLE_SIZE,
.ep0_mps = EP0_MPS,
.ep_mps = EP_MPS,
.speed_idx = DT_ENUM_IDX(DT_DRV_INST(0), maximum_speed),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maximum_speed property is not available from all DT.
In old driver, property is check before access it:

#if DT_INST_NODE_HAS_PROP(0, maximum_speed)
#define USB_MAXIMUM_SPEED DT_INST_PROP(0, maximum_speed)
#endif

Copy link
Member

@erwango erwango Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.speed_idx = DT_ENUM_IDX(DT_DRV_INST(0), maximum_speed),
.speed_idx = DT_ENUM_IDX_OR(DT_DRV_INST(0), maximum_speed, 1),

@topisani topisani force-pushed the udc-stm32 branch 2 times, most recently from 480c9c5 to cfee3f3 Compare December 6, 2024 09:23
@@ -260,6 +243,28 @@ void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
uint32_t rx_count = HAL_PCD_EP_GetRxCount(hpcd, epnum);
struct udc_stm32_data *priv = hpcd2data(hpcd);
struct udc_stm32_msg msg = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I caught an issue where this check would trigger ZLP expected (only appeared on usbotg_hs), as multiple messages arriving before being handled would be merged. Storing the rx length in the msgq fixed this. It doubles the size of the messages, but I dont see a way around it. The cdc_acm test still fails on usbotg_hs.

if (buf->len == 0) {
LOG_DBG("s-in-status");
next_stage = CTRL_PIPE_STAGE_SETUP;
} else {
LOG_WRN("ZLP expected");
next_stage = CTRL_PIPE_STAGE_ERROR;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I think this could potentially fix a couple of issues seen by @fpistm (particularly on Windows). He'll only be able to answer on monday though.

The cdc_acm test still fails on usbotg_hs.

Which board are you using for this test ?

Copy link
Contributor Author

@topisani topisani Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which board are you using for this test ?

Mainly testing using stm32f769i_disco, but i see the same on a portenta_h7. I may be able to test the fs interface on the h7 to verify that it only happens on hs, but the portenta is mounted in a custom board where its a bit more difficult to perform isolated tests.
Just to clarify: when running the cdc_acm sample, descriptors are exchanged without issue, but no characters are echoed back. I am currently doing some debugging with wireshark, i will report a separate issue once i have more context.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@topisani
I experienced the same problem of characters not being echoed back with the stm32h747i_disco board, but it ran smoothly on the nucleo_h743zi board, which means it only occurs on HS.

Copy link
Contributor Author

@topisani topisani Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have now also confirmed that it works on the usbotg_fs interface on stm32f769i_disco, and not on usbotg_hs on the same board. This issue with HS also exists on main.

run clang-format on udc_stm32.c

Signed-off-by: Tobias Pisani <[email protected]>
If both new and old stm32 usb drivers are enabled, the HAL callbacks
will raise multiple definition linker errors.  By adding this kconfig
dependency, this will be reported as a kconfig warning that should be
easier to understand.  This will happen when trying to run samples with
-DCONF_FILE=usbd_next_prj.conf for boards that automatically enable the
legacy usb stack (e.g. arduino_portenta/stm32h747xx/m7).

Signed-off-by: Tobias Pisani <[email protected]>
All HAL callback handling is offloaded to a separate thread, as they
involve non isr-compatible operations such as mutexes.

Fixes zephyrproject-rtos#61464

Signed-off-by: Tobias Pisani <[email protected]>
This fixes usbd_caps_speed(), which is used in sample_usbd_init, and the
documentation. Without it, no high speed configuration would be added.

Signed-off-by: Tobias Pisani <[email protected]>
On USB HS, the previous lower limit of 64 is too small, the value
160 has been chosen apparently through trial and error.

See 1204aa2 for the original implementation in the old device driver.

Signed-off-by: Tobias Pisani <[email protected]>
@topisani
Copy link
Contributor Author

topisani commented Dec 7, 2024

I found the usbotg_hs issue - porting this fix to device-next helped: #62530
Like the original related issues and PRs, i cannot find an explanation as to why 160 works as a minimum, so i kept the comment that it was chosen through trial and error.

For what it's worth, all issues I have encountered through testing have now been fixed.

@dsch
Copy link
Contributor

dsch commented Dec 11, 2024

Thank you @topisani for your great work!

Is it also required to push request to udc_ep_enqueue() on the message queue?

Similar as in NRF driver:

static int udc_nrf_ep_enqueue(const struct device *dev,
struct udc_ep_config *cfg,
struct net_buf *buf)
{
struct udc_nrf_evt evt = {
.type = UDC_NRF_EVT_XFER,
.ep = cfg->addr,
};
udc_buf_put(cfg, buf);
if (cfg->addr == USB_CONTROL_EP_IN && buf->len == 0) {
if (udc_nrf_fake_status_in(dev)) {
return 0;
}
}
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
return 0;
}

@@ -6,6 +6,7 @@ config UDC_STM32
depends on DT_HAS_ST_STM32_OTGFS_ENABLED \
|| DT_HAS_ST_STM32_OTGHS_ENABLED \
|| DT_HAS_ST_STM32_USB_ENABLED
depends on !USB_DC_STM32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jfischer-no I guess this issue is wider than STM32 and related to #81308. Are you fine this line ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also a bit specific to this driver though - at least theoretically it should be possible with multi-instance compatible controllers to use one with the new api, and one with the old. But the two stm32 drivers specifically cannot coexist because of the Hal callback definitions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, Thanks. Fine for me then

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

USB_DC_STM32 cannot be used here, once deprecated it should fail in CI.

@topisani
Copy link
Contributor Author

Is it also required to push request to udc_ep_enqueue() on the message queue?

I considered it, but as far as I can tell it should be ok. I will take another look

@@ -6,6 +6,7 @@ config UDC_STM32
depends on DT_HAS_ST_STM32_OTGFS_ENABLED \
|| DT_HAS_ST_STM32_OTGHS_ENABLED \
|| DT_HAS_ST_STM32_USB_ENABLED
depends on !USB_DC_STM32
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

USB_DC_STM32 cannot be used here, once deprecated it should fail in CI.

@@ -60,6 +60,18 @@ struct udc_stm32_config {
uint16_t ep_mps;
};

enum udc_stm32_msg_kind {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kind? enum udc_stm32_msg_type!

Comment on lines +138 to +146
void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd)
{
struct udc_stm32_data *priv = hpcd2data(hpcd);
struct udc_stm32_msg msg = {.kind = UDC_STM32_MSG_SETUP};
int err = k_msgq_put(&priv->msgq_data, &msg, K_NO_WAIT);

if (err < 0) {
LOG_ERR("UDC Message queue overrun");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is actually better way to handle it using k_event, take a look at how dwc2 driver handle it, or just start to use it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The driver specifically needs to handle separate RX callbacks individually. I dont see a way to do this using events, as multiple callbacks for one endpoint can occur between the handler thread running. I assume this is the reason why the udc_nrf driver also does it this way.

Comment on lines +255 to +257
int err = k_msgq_put(&priv->msgq_data, &msg, K_NO_WAIT);

if (err < 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No hidden calls, no undefined behavior.

	int err;

	err = k_msgq_put(&priv->msgq_data, &msg, K_NO_WAIT);
	if (err) {

*/
words = MAX(0x40, cfg->ep_mps / 4);
words = MAX(160, cfg->ep_mps / 4);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On USB HS, the previous lower limit of 64 is too small, the value
160 has been chosen apparently through trial and error.

At a minimum, this value should be selected based on the controller type and capabilities, not "trial and error".

Copy link
Contributor Author

@topisani topisani Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the discussion here: #62530
I've been digging through documentation and related issues from when this change was introduced on the old usb device driver, and i cannot seem to find any satisfactory explanation other than "this value works". This configuration is what worked in the old driver, which has been used a lot in practice. But maybe someone at ST can help settle where this value comes from (@erwango)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately @Desvauxm-st left the company. So I don't have more information other than what is available in the PR. @marwaiehm-st maybe ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, we tried to find a satisfactory explanation other than "this value works", but until now, we haven't found an explanation for why the minimum value of FIFO RX needs to be 160.

@@ -29,20 +29,20 @@
LOG_MODULE_REGISTER(udc_stm32, CONFIG_UDC_DRIVER_LOG_LEVEL);

#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs)
#define DT_DRV_COMPAT st_stm32_otghs
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reformatting of the code that follows the coding-style.

@fpistm
Copy link
Contributor

fpistm commented Dec 16, 2024

Hi,
here the result of all my tests using samples/subsys/usb/cdc_acm with -DCONF_FILE=usbd_next_prj.conf and sent a lorem ipsum buffer of 1024 bytes several times.

In any case it fixes the assert issue reported here: #61464.

series boards controller cdc_acm sample plug/unplug comment
F2 nucleo_f207zg st,stm32-otgfs 🟢 🟢
F4 nucleo_f429zi st,stm32-otgfs 🟢 🟢
F7 stm32f746g_disco st,stm32-otgfs 🟢 🟢
H7 nucleo_h723zg st,stm32-otghs 🟢 🟢
L4 b_l4s5i_iot01a st,stm32-otgfs 🟢 🟢
U5 nucleo_u575zi_q st,stm32-otgfs 🟢 🟢
F3 nucleo_f303re (1) st,stm32-usb 🟠 🔴 dts updated. See status here
G4 nucleo_g431rb (1) st,stm32-usb 🟠 🔴 dts updated. Same status than nucleo_f303re except no patch needed to get a fixed baudrate value (115200)
H5 nucleo_h563zi st,stm32-usb 🟠 using patch 🔴 Bus fault #76250. Fixed baudrate value reported (115200) using patch
WB nucleo_wb55rg st,stm32-usb 🟠 🔴 Same status than nucleo_f303re.

Note

  1. shield used to add USB connector
Workaround 1
@@ -458,11 +458,14 @@ static int usbd_cdc_acm_ctd(struct usbd_class_data *const c_data,
 		if (setup->wLength != len) {
 			errno = -ENOTSUP;
 			return 0;
 		}

-		memcpy(&data->line_coding, buf->data, len);
+		if (buf->data) {
+			printk("SET_LINE_CODING buf->data not null\n");
+			memcpy(&data->line_coding, buf->data, len);
+		}
 		cdc_acm_update_uart_cfg(data);
 		usbd_msg_pub_device(uds_ctx, USBD_MSG_CDC_ACM_LINE_CODING, dev);
 		return 0;

 	case SET_CONTROL_LINE_STATE:

dts updated
+
+zephyr_udc0: &usb {
+	pinctrl-0 = <&usb_dm_pa11 &usb_dp_pa12>;
+	pinctrl-names = "default";
+	status = "okay";
+};

nucleo_f303re

Assert enabled
log
*** Booting Zephyr OS build v4.0.0-1636-gddeb51a89cfd ***
[00:00:00.001,000] <err> usbd_core: Failed to set default address after bus reset:
[00:00:00.001,000] <inf> cdc_acm_echo: USBD message: Bus reset:
[00:00:00.001,000] <inf> cdc_acm_echo: USBD message: Stack error:
[00:00:00.001,000] <inf> cdc_acm_echo: USB device support enabled:
[00:00:00.001,000] <inf> cdc_acm_echo: Wait for DTR:
[00:00:00.004,000] <inf> cdc_acm_echo: USBD message: Device suspended:
[00:00:00.112,000] <inf> cdc_acm_echo: USBD message: Device resumed:
[00:00:00.167,000] <inf> cdc_acm_echo: USBD message: Bus reset:
[00:00:00.281,000] <inf> cdc_acm_echo: USBD message: Bus reset:
[00:00:00.362,000] <inf> cdc_acm_echo: USBD message: New device configuration:
ASSERTION FAIL [net_buf_simple_tailroom(buf) >= len] @ WEST_TOPDIR/zephyr/lib/net_buf/buf_simple.c:62
[00:00:00.363,000] <err> os: r0/a1:  0x00000004  r1/a2:  0x0000003e  r2/a3:  0x00000005:
[00:00:00.363,000] <err> os: r3/a4:  0x00000004 r12/ip:  0x20003160 r14/lr:  0x08003b5d:
[00:00:00.363,000] <err> os:  xpsr:  0x01000000:
[00:00:00.363,000] <err> os: Faulting instruction address (r15/pc): 0x0800d228:
[00:00:00.363,000] <err> os: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0:
[00:00:00.363,000] <err> os: Current thread: 0x20000f60 (unknown):
[00:00:00.444,000] <err> os: Halting system:

Plug/unplug

Important

On linux, second plug produce this issue (re-enumeration):
On Windows, first plug (enumeration), means CDC port never created.

log
[00:00:07.187,000] <err> udc: Failed to allocate net_buf 2
[00:00:07.187,000] <err> usbd_ch9: Buffer for data|status is missing
[00:00:07.187,000] <err> usbd_ch9: Malformed setup packet
[00:00:07.188,000] <err> udc: Failed to allocate net_buf 2
[00:00:07.188,000] <err> usbd_ch9: Buffer for data|status is missing
[00:00:07.188,000] <err> usbd_ch9: Malformed setup packet
[00:00:07.188,000] <err> udc: Failed to allocate net_buf 2
[00:00:07.188,000] <err> usbd_ch9: Buffer for data|status is missing
[00:00:07.188,000] <err> usbd_ch9: Malformed setup packet
[00:00:07.188,000] <err> udc: Failed to allocate net_buf 255
[00:00:07.188,000] <err> usbd_ch9: Buffer for data|status is missing
[00:00:07.188,000] <err> usbd_ch9: Malformed setup packet
[00:00:07.188,000] <err> udc: Failed to allocate net_buf 255
[00:00:07.188,000] <err> usbd_ch9: Buffer for data|status is missing
[00:00:07.188,000] <err> usbd_ch9: Malformed setup packet
[00:00:07.189,000] <err> udc: Failed to allocate net_buf 255
[00:00:07.189,000] <err> usbd_ch9: Buffer for data|status is missing
[00:00:07.189,000] <err> usbd_ch9: Malformed setup packet
[00:00:07.189,000] <err> udc: Failed to allocate net_buf 2
[00:00:07.189,000] <err> usbd_ch9: Buffer for data|status is missing
[00:00:07.189,000] <err> usbd_ch9: Malformed setup packet
[00:00:07.189,000] <err> udc: Failed to allocate net_buf 2
[00:00:07.189,000] <err> usbd_ch9: Buffer for data|status is missing
[00:00:07.189,000] <err> usbd_ch9: Malformed setup packet
[00:00:07.190,000] <err> udc: Failed to allocate net_buf 2
[00:00:07.190,000] <err> usbd_ch9: Buffer for data|status is missing
[00:00:07.190,000] <err> usbd_ch9: Malformed setup packet
[00:00:07.193,000] <err> udc: Failed to allocate net_buf 0
[00:00:07.193,000] <err> usbd_ch9: Buffer for data|status is missing
[00:00:07.193,000] <err> usbd_ch9: Malformed setup packet

@erwango
Copy link
Member

erwango commented Dec 16, 2024

here the result of all my tests using samples/subsys/usb/cdc_acm with -DCONF_FILE=usbd_next_prj.conf and sent a lorem ipsum buffer of 1024 bytes several times.

Thanks @fpistm. Hence to summarize, this PR actually fixes #61464, but some issues remain, likely all linked/close to #76250.

For clarity, I'll open a new issue to gather all this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: USB Universal Serial Bus platform: STM32 ST Micro STM32
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants